﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Globalization;
using System.Windows.Forms;

namespace OffInvoiceExcelTemplate
{
    public class Calculos
    {
        #region Fields

        FieldFormats ff;
        CultureInfo culture;
        NumberFormatInfo num;

        #endregion

        #region Constructors

        public Calculos(FieldFormats ff)
        {
            this.ff = ff;

            culture = culture = new CultureInfo(Globals.ThisWorkbook.ThisApplication.LanguageSettings.get_LanguageID(Microsoft.Office.Core.MsoAppLanguageID.msoLanguageIDUI));
            num = culture.NumberFormat;
        }

        #endregion

        #region total fields


        public double totalGrossAmount = 0.0;
        public double generalDiscountBase = 0.0;
        public double totalGeneralDiscount = 0.0;
        public double totalGrossAmountBeforeTaxes = 0.0;
        public double totalTaxesOutputs = 0.0;
        public double totalTaxesWithHeld = 0.0;
        public double totalAmount = 0.0;
        public Taxes taxes = new Taxes();
        public Taxes taxesw;

        #endregion

        #region detail fields

        public string concepto = string.Empty;
        public double units = 0.0;
        public double precio = 0.0;
        public double desc = 0.0;
        public double iva = 0.0;
        public double irpf = 0.0;
        public double baseimp = 0.0;
        public double ammount = 0.0;

        #endregion

        #region discount fields

        public string discountdesc = string.Empty;
        public double discRate = 0.0;
        public double dicsammount = 0.0;

        #endregion

        # region Public Methods

        /// <summary>
        ///  recibe una matriz de 6 columnas con los valores de la tabla detalles
        /// el orden de los valores es: concepto, unidades, precio, descuento, iva, irpf,
        /// </summary>
        /// <param name="rows">matriz con los valores de la tabla detalles</param>
        /// <returns>una matriz con los valores calculados baseimponible e importe</returns>
        public object[,] computeDetails(object[,] rows)
        {

            object[,] results = new object[rows.GetLength(0), 11];

            try
            {
                for (int i = 0; i < rows.GetLength(0); i++)
                {
                    initDetailFields(rows, i);
                    double costo =  Calculos.redondeo(units * precio);
                    baseimp =  Calculos.redondeo(costo - (costo * desc / 100));
                    ammount =  Calculos.redondeo(baseimp + (baseimp * iva / 100) - (baseimp * irpf / 100));
                    totalGrossAmount +=  Calculos.redondeo(baseimp);
                    results[i, 0] = rows[i, 0];
                    results[i, 1] = concepto;
                    results[i, 2] = units;
                    results[i, 3] = precio;
                    results[i, 4] = costo;
                    results[i, 5] = desc;
                    results[i, 6] = costo * desc / 100;
                    results[i, 7] = baseimp;
                    results[i, 8] = iva;
                    results[i, 9] = irpf;
                    results[i, 10] = ammount;



                    if (iva > 0)
                    {
                        taxes.BaseImponiblePrefix = string.Concat(Loader.LoadLabel(ff.messagePath, "InfoTaxablebaxe"), iva, "%", ")");
                        taxes.AmmountPrefix = string.Concat(Loader.LoadLabel(ff.messagePath, "Infotaxamount"), iva, ")");
                        taxes.setTaxes(iva.ToString(), baseimp * iva / 100, baseimp);  
                        totalTaxesOutputs = taxes.Totaltaxes;
                    }

                    if (irpf > 0)
                    {
                        if (taxesw == null)
                        {
                            taxesw = new Taxes();
                        }
                        taxesw.setTaxes(irpf.ToString(), baseimp * irpf / 100, baseimp);
                        totalTaxesWithHeld =  taxesw.Totaltaxes;
                    }

                  
                    
                    totalGrossAmountBeforeTaxes = Calculos.redondeo(totalGrossAmount - totalGeneralDiscount);
                    totalAmount = totalGrossAmountBeforeTaxes + totalTaxesOutputs - totalTaxesWithHeld;
                }
            }
            catch (Exception ex)
            {
                MessageBox.Show(Loader.LoadLabel(ff.messagePath, ex.Message), Loader.LoadLabel(ff.messagePath, "captionDetails"), MessageBoxButtons.OK, MessageBoxIcon.Stop, MessageBoxDefaultButton.Button1);
                throw ex;
            }

            return results;

        }

        /// <summary>
        /// recibe una matriz de 3 columnas con los valores de la tabla descuentos
        /// el orden de los valores es: descripcion,descuento
        /// </summary>
        /// <param name="rows">matriz con los valores de la tabla descuentos</param>
        /// <returns>una matriz con los valores y el importe</returns>
        public object[,] computeDiscount(object[,] rows)
        {
            generalDiscountBase = 0.0;
            totalGeneralDiscount = 0.0;
            dicsammount = 0.0;
            totalGrossAmountBeforeTaxes = 0.0;

            object[,] results = new object[rows.GetLength(0), 3];

            try
            {
                for (int i = 0; i < rows.GetLength(0); i++)
                {
                    initDiscountFields(rows, i);
                    dicsammount =  Calculos.redondeo((totalGrossAmount - totalGeneralDiscount) * discRate / 100);
                    totalGeneralDiscount += Calculos.redondeo(dicsammount);
                    results[i, 0] = discountdesc;
                    results[i, 1] = discRate;
                    results[i, 2] = dicsammount;
                }
                totalGrossAmountBeforeTaxes =  Calculos.redondeo(totalGrossAmount - totalGeneralDiscount);
                totalAmount = Calculos.redondeo(totalGrossAmountBeforeTaxes + totalTaxesOutputs - totalTaxesWithHeld);
            }
            catch (Exception ex)
            {
                MessageBox.Show(Loader.LoadLabel(ff.messagePath, ex.Message), Loader.LoadLabel(ff.messagePath, "captionDiscounts"), MessageBoxButtons.OK, MessageBoxIcon.Stop, MessageBoxDefaultButton.Button1);
                throw ex;
            }
            return results;
        }

        /// <summary>
        /// inicializa las variables para el calculo de los detalles
        /// </summary>
        /// <param name="rows">array que contiene los valores de los campos de la tabla de detalles</param>
        /// <param name="i">numemero de fila que se esta procesando</param>                      
        private void initDetailFields(object[,] rows, int i)
        {


            try
            {
                concepto = ff.description_Validating((string)rows[i, 1]);

                units = double.Parse(ff.ud_Validating((string)rows[i, 2]), num);

                precio = double.Parse(ff.price_Validating((string)rows[i, 3]), num);

                iva = double.Parse(ff.iva_Validating((string)rows[i, 5]), num);

                try
                {
                    desc = double.Parse(ff.discount_Validating((string)rows[i, 4]), num);
                }
                catch (Exception) { }

                try
                {
                    irpf = double.Parse(ff.irpf_Validating((string)rows[i, 6]), num);
                }
                catch (Exception) { }

            }
            catch (Exception ex)
            {
                throw ex;
            }



        }

        /// <summary>
        /// inicializa las variables para el calculo de los descuentos
        /// </summary>
        /// <param name="rows">array que contiene los valores de los campos de la tabla de descuentos</param>
        /// <param name="i">numemero de fila que se esta procesando</param>               
        private void initDiscountFields(object[,] rows, int i)
        {
            try
            {
                discountdesc = ff.description_Validating((string)rows[i, 0]);
                discRate = double.Parse(ff.generalDiscount_Validating((string)rows[i, 1]), num);
            }
            catch (Exception ex)
            {
                throw ex;
            }




        }

        /// <summary>
        /// Inicializa los valores de los totales de montobruto y descuentos generales
        /// </summary>
        /// <param name="tgrossamount"></param>
        /// <param name="tgeneraldisc"></param>
        private void initTotals(double tgrossamount, double tgeneraldisc)
        {
            this.totalGrossAmount = tgrossamount;
            this.generalDiscountBase = this.totalGrossAmount;
            this.totalGeneralDiscount = tgeneraldisc;


        }

        /// <summary>
        /// Actualiza los valores para los totales
        /// </summary>
        public void resetTotals()
        {
            totalGrossAmount = 0.0;
            generalDiscountBase = 0.0;
            totalGeneralDiscount = 0.0;
            totalTaxesOutputs = 0.0;
            totalTaxesWithHeld = 0.0;
            totalAmount = 0.0;
            concepto = string.Empty;
            units = 0.0;
            precio = 0.0;
            desc = 0.0;
            iva = 0.0;
            irpf = 0.0;
            baseimp = 0.0;
            ammount = 0.0;
            discountdesc = string.Empty;
            discRate = 0.0;
            dicsammount = 0.0;
            taxes.TaxesList = null;
            taxes.Totaltaxes = 0.0;
        }

        public void resetDiscounts()
        {
            totalAmount += totalGeneralDiscount;
            totalGeneralDiscount = 0;
        }

        public static double redondeo(double numero)
        {
            double multi = Math.Pow(10.0d, (double)2);

            if (numero < 0)
                return (-(long)((-numero * multi) + 0.5d)) / multi;
            else
                return ((long)((numero * multi) + 0.5d)) / multi;
        }

        #endregion
    }


    public class Taxes
    {


        #region fields

        private double totaltaxes = 0.0;
        private string taxType = string.Empty;
        private string ammountPrefix;
        private string baseImponiblePrefix;
        private double taxablebase = 0.0;
        private double taxamount = 0.0;
        private List<Taxes> taxesList;


        public double Totaltaxes
        {
            get { return totaltaxes; }
            set { totaltaxes = value; }
        }

        public string TaxType
        {
            get { return taxType; }
            set { taxType = value; }
        }

        public string AmmountPrefix
        {
            get { return ammountPrefix; }
            set { ammountPrefix = value; }
        }

        public string BaseImponiblePrefix
        {
            get { return baseImponiblePrefix; }
            set { baseImponiblePrefix = value;}
        }

        public double Taxablebase
        {
            get { return taxablebase; }
            set { taxablebase += value; }
        }

        public double Taxamount
        {
            get { return taxamount; }
            set { taxamount += value; }
        }

        public List<Taxes> TaxesList
        {
            get { return taxesList; }
            set { taxesList = value; }
        }

        #endregion

        # region Public Methods

        public void setTaxes(string taxType, double taxAmount, double taxableBase)
        {
            bool assigned = false;

            if (taxesList == null)
            {
                taxesList = new List<Taxes>();
            }


            foreach (Taxes tax in taxesList)
            {
                if (tax.taxType.ToLower().Equals(taxType.ToLower()))
                {
                    tax.Taxamount = taxAmount;
                    tax.Taxablebase = taxableBase;
                    assigned = true;
                }
            }

            if (!assigned)
            {
                Taxes tax = new Taxes();
                tax.TaxType = taxType;
                tax.Taxamount = taxAmount;
                tax.Taxablebase = taxableBase;
                tax.AmmountPrefix = taxType;
                tax.BaseImponiblePrefix = taxType;
                this.taxesList.Add(tax);
            }
            this.Totaltaxes += Calculos.redondeo(taxAmount);


        }

        #endregion
    }
}
